package org.zanateh.wargame.game.hexagon;

import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import org.anddev.andengine.entity.scene.Scene;
import org.zanateh.wargame.game.util.Coordinate;
import org.zanateh.wargame.graphics.HexagonDrawable;

import android.util.Pair;

public class HexField {
	
	public class NotInitializedException extends Exception {
		public NotInitializedException() {
			super();
			// TODO Auto-generated constructor stub
		}

		public NotInitializedException(String detailMessage, Throwable throwable) {
			super(detailMessage, throwable);
			// TODO Auto-generated constructor stub
		}

		public NotInitializedException(String detailMessage) {
			super(detailMessage);
			// TODO Auto-generated constructor stub
		}

		public NotInitializedException(Throwable throwable) {
			super(throwable);
			// TODO Auto-generated constructor stub
		}

		private static final long serialVersionUID = 2794873461138900277L;
	}
	
	private final float mWidth;
	private final float mHeight;
	private float mHexSideWidth = 0;
	private float mHexDistanceToEdge = 0; // Calculated distance from centre to midpoint on edge
	
	ArrayList<ArrayList<Hexagon>> mHexField = null;
	Map<Hexagon, Coordinate> mHexToCoordinateMap = null; 

	public HexField( float width, float height) {
		mWidth = width;
		mHeight = height;
	}
		
	public void loadHexMap(ArrayList<String> hexMap) {
		if( hexMap.size() == 0) {
			mHexField = new ArrayList<ArrayList<Hexagon>>(0);
			setHexSideLength(1);
		}
		else {
			int columnCount = hexMap.size();
			int rowCount = hexMap.get(0).length();
			setHexSideLength(calculateHexEdgeLength( mWidth, mHeight, columnCount, rowCount));
			
			initHexField(columnCount, rowCount);
			for(int i=0; i < columnCount; ++i) {
				String hexMapCol = hexMap.get(i);
				if(hexMapCol.length() != rowCount) {
					throw new IllegalArgumentException("Invalid map provided. Row count column 0 = " + rowCount + ", column " + i + " = " + hexMapCol.length());
				}
				
				for( int j=0; j < rowCount; ++j )
				{
					setHex(i,j, hexMapCol.charAt(j));
				}
			}
		}
	}
	
	public void setHexSideLength(float len)
	{
		mHexSideWidth = len;
		mHexDistanceToEdge = (float)(Math.sin(Math.toRadians(60))*mHexSideWidth);
	}
	
	public static float calculateHexEdgeLength(float width, float height,
			int columnCount, int rowCount) {
		float maxEdgeForCols = (float) (width / (1.5 * columnCount + 0.5));
		float maxEdgeForRows = (float) (height / (((2 * rowCount) + 1) * Math.sin(Math.toRadians(60))) );
		
		return Math.min(maxEdgeForRows, maxEdgeForCols);
	}

	public void generateHexField(int rowCount, int columnCount)
	{
		float sideLength = calculateHexEdgeLength( mWidth, mHeight, columnCount, rowCount);
		generateHexField(rowCount, columnCount, sideLength);
	}
	
	public void generateHexField(float sideLength)
	{
		int columnCount = calculateColumnCount(mWidth, sideLength);
		int rowCount = calculateRowCount(mHeight, sideLength);
		generateHexField(rowCount, columnCount, sideLength);
	}
		
	private void generateHexField(int rowCount, int columnCount, float hexSideLength)
	{
		setHexSideLength(hexSideLength);
		initHexField(columnCount, rowCount);
		
		for(int i = 0; i < columnCount; ++i) {
			for(int j = 0; j < rowCount; ++j) {
				setHex( i, j );
			}
		}
	}
	
	private void initHexField( int cols, int rows )
	{
		mHexField = new ArrayList<ArrayList<Hexagon>>(cols);
		for( int i=0; i < cols; ++i)
		{
			ArrayList<Hexagon> newRow = new ArrayList<Hexagon>(rows);
			for(int j=0; j < rows; ++j)
			{
				newRow.add(null);
			}
			mHexField.add(newRow);
		}
		mHexToCoordinateMap = new HashMap<Hexagon, Coordinate> ();
	}
	
	private void setHex( int col, int row ) 
	{
		setHex(col, row, 'A');
	}
	
	private void setHex( int col, int row, char hexType )
	{
		Hexagon hex = HexagonFactory.createHexagon(hexType);
		mHexField.get(col).set(row, hex);
		mHexToCoordinateMap.put(hex, new Coordinate(col, row));
		hex.setHexField(this);
	}
	
	public Coordinate getFieldCoordinatesForHex(Hexagon pHex) throws NotInitializedException
	{
		if( mHexToCoordinateMap == null ) {
			throw new NotInitializedException("HexField has not been initialized yet.");
		}
		
		if( pHex == null || !mHexToCoordinateMap.containsKey(pHex)) {
			throw new IllegalArgumentException("Provided hexagon not part of this hexfield or null.");
		}
		
		return mHexToCoordinateMap.get(pHex);
	}

	private Pair<Float, Float> getSceneCoordinatesForHex(int col, int row)
	{
		// x position is 1*C, 2.5C, 4C, ... (1 + (n-1)*1.5)*C
		float x = (float)(1 + col * 1.5) * mHexSideWidth;
		// y position is 1*b, 3*b, 5*b.. for first column, 2*b, 4*b, 6*b... for second
		float y;
		if(col - (col/2)*2 == 0) {
			y = (1 + (row * 2)) * mHexDistanceToEdge;
		}
		else {
			y = ((1 + row) * 2) * mHexDistanceToEdge;
		}
		
		Pair<Float, Float> retPair = new Pair<Float, Float>(x, y);
	
		return retPair;
	}
	
	public void attachToScene(Scene scene)
	{
		for(int i = 0; i < mHexField.size(); ++i) // columns 
		{
			ArrayList<Hexagon> column = mHexField.get(i);
			for(int j = 0; j < column.size(); ++j) 
			{
				HexagonDrawable drawHex = column.get(j).getDrawable();
				Pair<Float, Float> hexCoords = getSceneCoordinatesForHex(i,j);
				drawHex.setPositionAndSize( hexCoords.first, hexCoords.second, mHexSideWidth);
				scene.attachChild(drawHex);
				scene.registerTouchArea(drawHex);
			}
		}
	}
	
    public static int calculateColumnCount(float width, float hexSideWidth )
    {
		// How many columns? first col takes sw * 2, then each takes (sideWidth * 3/2)
		int columnCount = 0;
		if( width >= hexSideWidth * 2) {
			columnCount = 1 + (int)(Math.floor((width - (hexSideWidth*2)) / (hexSideWidth*1.5))); 
		}
    	
    	return columnCount;
    }

	public static int calculateRowCount(float height, float hexSideWidth) {
		float hexDistanceToEdge = (float)(Math.sin(Math.toRadians(60))*hexSideWidth);

		if( height < hexDistanceToEdge *3) {
			return 0;
		}
		else {
			return 1 + (int)((height - hexDistanceToEdge * 3) / (hexDistanceToEdge * 2));
		}
		
	}

	public int getColumnCount()
	{
		return mHexField.size();
	}
	
	public int getRowCount()
	{
		if( mHexField.size() == 0) return 0;
		return mHexField.get(0).size();
	}

	public Hexagon getHex(int i, int j) throws NotInitializedException {
		if( mHexField == null )
		{
			throw new NotInitializedException("HexField has not been initialized yet.");
		}
		
		if(i >= mHexField.size()) {
			throw new IndexOutOfBoundsException("HexField contains " + mHexField.size() + " columns, asked for column " + i );
		}
		
		ArrayList<Hexagon> column = mHexField.get(i);
		if( j >= column.size()) {
			throw new IndexOutOfBoundsException("HexField column " + i + " contains " + mHexField.size() + " rows, asked for row " + j );
		}
		
		return column.get(j);
	}
	
}
